﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ChromaCode.Models;
using ChromaCode.Extensions;
using System.Windows.Documents;

namespace ChromaCode.Services
{
    /// <summary>
    /// Service for applying a set of word based rules to a range of text
    /// </summary>
    internal class WordRuleService : RuleBaseService<WordRule>
    {
        #region Public

        /// <summary>
        /// Initialise the word rule service and start highlighting the supplied paragraph
        /// </summary>
        /// <param name="rules">A list of rules to use</param>
        /// <param name="startParagraph">The paragraph to apply the highlighting to</param>
        public WordRuleService(List<Rule> rules, Paragraph startParagraph)
        {
            if (rules != null)
            {
                StartParagraph = startParagraph;

                // Get any word rules from the list of supplied rules
                Rules = rules.Where(x => x.TypeOfRule == RuleType.WordHighlighter).Select(y => (WordRule)y).ToList();

                // If we have rules and a text range then start the highlight process
                if ((Rules != null) && (Rules.Count > 0) && (StartParagraph != null))
                {
                    DoHighlighting();
                }
            }
        }

        #endregion Public

        #region Private

        /// <summary>
        /// Main function that carries out the highlight process
        /// </summary>
        private void DoHighlighting()
        {
            TextRange tr = new TextRange(StartParagraph.ContentStart, StartParagraph.ContentEnd);
            tr.ClearAllProperties();
            string text = tr.Text;
            int startPos = -1; /* -1 means no start position is set until we find the next letter */
            int endPos = text.Length - 1;
            StringBuilder word = new StringBuilder();

            // Strip all existing formatting from the range
            tr.ClearAllProperties();

            // Loop until we read the end position
            for (int charIndex = 0; charIndex <= endPos; charIndex++)
            {
                if (char.IsLetter(text[charIndex]))
                {
                    // We have found a letter so add it to the current word
                    word.Append(text[charIndex]);

                    // If we have no start position yet then set it here
                    if (startPos < 0)
                    {
                        startPos = charIndex;
                    }
                }
                else
                {
                    // This is not a letter so reset everything ready for the next word
                    word.Clear();
                    startPos = -1; /* -1 means no start position is set until we find the next letter */
                }

                // Search the list of word rules for a match to our current word
                WordRule foundRule = Rules.FindWordRule(word.ToString());

                if (foundRule != null)
                {
                    // We found a possible matching rule. We can only be sure if the next character on is either the end of
                    // the text range or is not a letter
                    if ((charIndex + 1 > endPos) || (!char.IsLetter(text[charIndex + 1])))
                    {
                        // We now now for sure that we have a matching so we will create a new highlight element and
                        // then reset everything ready for scanning the next word
                        HighlightElement element = new HighlightElement(tr.Start.GetInsertationPointAtOffset(startPos),
                                                                        tr.Start.GetInsertationPointAtOffset(charIndex + 1), foundRule);
                        HighlightElements.Add(element);
                        word.Clear();
                        startPos = -1; /* -1 means no start position is set until we find the next letter */
                    }
                }
            }
        }

        #endregion Private
    }
}
